/*
 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */


package javax.swing.plaf.basic;


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.*;
import javax.swing.border.Border;
import java.beans.*;
import sun.swing.DefaultLookup;


/**
 * Divider used by BasicSplitPaneUI. Subclassers may wish to override
 * paint to do something more interesting.
 * The border effect is drawn in BasicSplitPaneUI, so if you don't like
 * that border, reset it there.
 * To conditionally drag from certain areas subclass mousePressed and
 * call super when you wish the dragging to begin.
 * <p>
 * <strong>Warning:</strong>
 * Serialized objects of this class will not be compatible with
 * future Swing releases. The current serialization support is
 * appropriate for short term storage or RMI between applications running
 * the same version of Swing.  As of 1.4, support for long term storage
 * of all JavaBeans&trade;
 * has been added to the <code>java.beans</code> package.
 * Please see {@link java.beans.XMLEncoder}.
 *
 * @author Scott Violet
 */
public class BasicSplitPaneDivider extends Container
    implements PropertyChangeListener {

  /**
   * Width or height of the divider based on orientation
   * BasicSplitPaneUI adds two to this.
   */
  protected static final int ONE_TOUCH_SIZE = 6;
  protected static final int ONE_TOUCH_OFFSET = 2;

  /**
   * Handles mouse dragging message to do the actual dragging.
   */
  protected DragController dragger;

  /**
   * UI this instance was created from.
   */
  protected BasicSplitPaneUI splitPaneUI;

  /**
   * Size of the divider.
   */
  protected int dividerSize = 0; // default - SET TO 0???

  /**
   * Divider that is used for noncontinuous layout mode.
   */
  protected Component hiddenDivider;

  /**
   * JSplitPane the receiver is contained in.
   */
  protected JSplitPane splitPane;

  /**
   * Handles mouse events from both this class, and the split pane.
   * Mouse events are handled for the splitpane since you want to be able
   * to drag when clicking on the border of the divider, which is not
   * drawn by the divider.
   */
  protected MouseHandler mouseHandler;

  /**
   * Orientation of the JSplitPane.
   */
  protected int orientation;

  /**
   * Button for quickly toggling the left component.
   */
  protected JButton leftButton;

  /**
   * Button for quickly toggling the right component.
   */
  protected JButton rightButton;

  /**
   * Border.
   */
  private Border border;

  /**
   * Is the mouse over the divider?
   */
  private boolean mouseOver;

  private int oneTouchSize;
  private int oneTouchOffset;

  /**
   * If true the one touch buttons are centered on the divider.
   */
  private boolean centerOneTouchButtons;


  /**
   * Creates an instance of BasicSplitPaneDivider. Registers this
   * instance for mouse events and mouse dragged events.
   */
  public BasicSplitPaneDivider(BasicSplitPaneUI ui) {
    oneTouchSize = DefaultLookup.getInt(ui.getSplitPane(), ui,
        "SplitPane.oneTouchButtonSize", ONE_TOUCH_SIZE);
    oneTouchOffset = DefaultLookup.getInt(ui.getSplitPane(), ui,
        "SplitPane.oneTouchButtonOffset", ONE_TOUCH_OFFSET);
    centerOneTouchButtons = DefaultLookup.getBoolean(ui.getSplitPane(),
        ui, "SplitPane.centerOneTouchButtons", true);
    setLayout(new DividerLayout());
    setBasicSplitPaneUI(ui);
    orientation = splitPane.getOrientation();
    setCursor((orientation == JSplitPane.HORIZONTAL_SPLIT) ?
        Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR) :
        Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
    setBackground(UIManager.getColor("SplitPane.background"));
  }

  private void revalidateSplitPane() {
    invalidate();
    if (splitPane != null) {
      splitPane.revalidate();
    }
  }

  /**
   * Sets the SplitPaneUI that is using the receiver.
   */
  public void setBasicSplitPaneUI(BasicSplitPaneUI newUI) {
    if (splitPane != null) {
      splitPane.removePropertyChangeListener(this);
      if (mouseHandler != null) {
        splitPane.removeMouseListener(mouseHandler);
        splitPane.removeMouseMotionListener(mouseHandler);
        removeMouseListener(mouseHandler);
        removeMouseMotionListener(mouseHandler);
        mouseHandler = null;
      }
    }
    splitPaneUI = newUI;
    if (newUI != null) {
      splitPane = newUI.getSplitPane();
      if (splitPane != null) {
        if (mouseHandler == null) {
          mouseHandler = new MouseHandler();
        }
        splitPane.addMouseListener(mouseHandler);
        splitPane.addMouseMotionListener(mouseHandler);
        addMouseListener(mouseHandler);
        addMouseMotionListener(mouseHandler);
        splitPane.addPropertyChangeListener(this);
        if (splitPane.isOneTouchExpandable()) {
          oneTouchExpandableChanged();
        }
      }
    } else {
      splitPane = null;
    }
  }


  /**
   * Returns the <code>SplitPaneUI</code> the receiver is currently
   * in.
   */
  public BasicSplitPaneUI getBasicSplitPaneUI() {
    return splitPaneUI;
  }


  /**
   * Sets the size of the divider to <code>newSize</code>. That is
   * the width if the splitpane is <code>HORIZONTAL_SPLIT</code>, or
   * the height of <code>VERTICAL_SPLIT</code>.
   */
  public void setDividerSize(int newSize) {
    dividerSize = newSize;
  }


  /**
   * Returns the size of the divider, that is the width if the splitpane
   * is HORIZONTAL_SPLIT, or the height of VERTICAL_SPLIT.
   */
  public int getDividerSize() {
    return dividerSize;
  }


  /**
   * Sets the border of this component.
   *
   * @since 1.3
   */
  public void setBorder(Border border) {
    Border oldBorder = this.border;

    this.border = border;
  }

  /**
   * Returns the border of this component or null if no border is
   * currently set.
   *
   * @return the border object for this component
   * @see #setBorder
   * @since 1.3
   */
  public Border getBorder() {
    return border;
  }

  /**
   * If a border has been set on this component, returns the
   * border's insets, else calls super.getInsets.
   *
   * @return the value of the insets property.
   * @see #setBorder
   */
  public Insets getInsets() {
    Border border = getBorder();

    if (border != null) {
      return border.getBorderInsets(this);
    }
    return super.getInsets();
  }

  /**
   * Sets whether or not the mouse is currently over the divider.
   *
   * @param mouseOver whether or not the mouse is currently over the divider
   * @since 1.5
   */
  protected void setMouseOver(boolean mouseOver) {
    this.mouseOver = mouseOver;
  }

  /**
   * Returns whether or not the mouse is currently over the divider
   *
   * @return whether or not the mouse is currently over the divider
   * @since 1.5
   */
  public boolean isMouseOver() {
    return mouseOver;
  }

  /**
   * Returns dividerSize x dividerSize
   */
  public Dimension getPreferredSize() {
    // Ideally this would return the size from the layout manager,
    // but that could result in the layed out size being different from
    // the dividerSize, which may break developers as well as
    // BasicSplitPaneUI.
    if (orientation == JSplitPane.HORIZONTAL_SPLIT) {
      return new Dimension(getDividerSize(), 1);
    }
    return new Dimension(1, getDividerSize());
  }

  /**
   * Returns dividerSize x dividerSize
   */
  public Dimension getMinimumSize() {
    return getPreferredSize();
  }


  /**
   * Property change event, presumably from the JSplitPane, will message
   * updateOrientation if necessary.
   */
  public void propertyChange(PropertyChangeEvent e) {
    if (e.getSource() == splitPane) {
      if (e.getPropertyName() == JSplitPane.ORIENTATION_PROPERTY) {
        orientation = splitPane.getOrientation();
        setCursor((orientation == JSplitPane.HORIZONTAL_SPLIT) ?
            Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR) :
            Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
        revalidateSplitPane();
      } else if (e.getPropertyName() == JSplitPane.
          ONE_TOUCH_EXPANDABLE_PROPERTY) {
        oneTouchExpandableChanged();
      }
    }
  }


  /**
   * Paints the divider.
   */
  public void paint(Graphics g) {
    super.paint(g);

    // Paint the border.
    Border border = getBorder();

    if (border != null) {
      Dimension size = getSize();

      border.paintBorder(this, g, 0, 0, size.width, size.height);
    }
  }


  /**
   * Messaged when the oneTouchExpandable value of the JSplitPane the
   * receiver is contained in changes. Will create the
   * <code>leftButton</code> and <code>rightButton</code> if they
   * are null. invalidates the receiver as well.
   */
  protected void oneTouchExpandableChanged() {
    if (!DefaultLookup.getBoolean(splitPane, splitPaneUI,
        "SplitPane.supportsOneTouchButtons", true)) {
      // Look and feel doesn't want to support one touch buttons, bail.
      return;
    }
    if (splitPane.isOneTouchExpandable() &&
        leftButton == null &&
        rightButton == null) {
            /* Create the left button and add an action listener to
               expand/collapse it. */
      leftButton = createLeftOneTouchButton();
      if (leftButton != null) {
        leftButton.addActionListener(new OneTouchActionHandler(true));
      }


            /* Create the right button and add an action listener to
               expand/collapse it. */
      rightButton = createRightOneTouchButton();
      if (rightButton != null) {
        rightButton.addActionListener(new OneTouchActionHandler
            (false));
      }

      if (leftButton != null && rightButton != null) {
        add(leftButton);
        add(rightButton);
      }
    }
    revalidateSplitPane();
  }


  /**
   * Creates and return an instance of JButton that can be used to
   * collapse the left component in the split pane.
   */
  protected JButton createLeftOneTouchButton() {
    JButton b = new JButton() {
      public void setBorder(Border b) {
      }

      public void paint(Graphics g) {
        if (splitPane != null) {
          int[] xs = new int[3];
          int[] ys = new int[3];
          int blockSize;

          // Fill the background first ...
          g.setColor(this.getBackground());
          g.fillRect(0, 0, this.getWidth(),
              this.getHeight());

          // ... then draw the arrow.
          g.setColor(Color.black);
          if (orientation == JSplitPane.VERTICAL_SPLIT) {
            blockSize = Math.min(getHeight(), oneTouchSize);
            xs[0] = blockSize;
            xs[1] = 0;
            xs[2] = blockSize << 1;
            ys[0] = 0;
            ys[1] = ys[2] = blockSize;
            g.drawPolygon(xs, ys, 3); // Little trick to make the
            // arrows of equal size
          } else {
            blockSize = Math.min(getWidth(), oneTouchSize);
            xs[0] = xs[2] = blockSize;
            xs[1] = 0;
            ys[0] = 0;
            ys[1] = blockSize;
            ys[2] = blockSize << 1;
          }
          g.fillPolygon(xs, ys, 3);
        }
      }

      // Don't want the button to participate in focus traversable.
      public boolean isFocusTraversable() {
        return false;
      }
    };
    b.setMinimumSize(new Dimension(oneTouchSize, oneTouchSize));
    b.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
    b.setFocusPainted(false);
    b.setBorderPainted(false);
    b.setRequestFocusEnabled(false);
    return b;
  }


  /**
   * Creates and return an instance of JButton that can be used to
   * collapse the right component in the split pane.
   */
  protected JButton createRightOneTouchButton() {
    JButton b = new JButton() {
      public void setBorder(Border border) {
      }

      public void paint(Graphics g) {
        if (splitPane != null) {
          int[] xs = new int[3];
          int[] ys = new int[3];
          int blockSize;

          // Fill the background first ...
          g.setColor(this.getBackground());
          g.fillRect(0, 0, this.getWidth(),
              this.getHeight());

          // ... then draw the arrow.
          if (orientation == JSplitPane.VERTICAL_SPLIT) {
            blockSize = Math.min(getHeight(), oneTouchSize);
            xs[0] = blockSize;
            xs[1] = blockSize << 1;
            xs[2] = 0;
            ys[0] = blockSize;
            ys[1] = ys[2] = 0;
          } else {
            blockSize = Math.min(getWidth(), oneTouchSize);
            xs[0] = xs[2] = 0;
            xs[1] = blockSize;
            ys[0] = 0;
            ys[1] = blockSize;
            ys[2] = blockSize << 1;
          }
          g.setColor(Color.black);
          g.fillPolygon(xs, ys, 3);
        }
      }

      // Don't want the button to participate in focus traversable.
      public boolean isFocusTraversable() {
        return false;
      }
    };
    b.setMinimumSize(new Dimension(oneTouchSize, oneTouchSize));
    b.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
    b.setFocusPainted(false);
    b.setBorderPainted(false);
    b.setRequestFocusEnabled(false);
    return b;
  }


  /**
   * Message to prepare for dragging. This messages the BasicSplitPaneUI
   * with startDragging.
   */
  protected void prepareForDragging() {
    splitPaneUI.startDragging();
  }


  /**
   * Messages the BasicSplitPaneUI with dragDividerTo that this instance
   * is contained in.
   */
  protected void dragDividerTo(int location) {
    splitPaneUI.dragDividerTo(location);
  }


  /**
   * Messages the BasicSplitPaneUI with finishDraggingTo that this instance
   * is contained in.
   */
  protected void finishDraggingTo(int location) {
    splitPaneUI.finishDraggingTo(location);
  }


  /**
   * MouseHandler is responsible for converting mouse events
   * (released, dragged...) into the appropriate DragController
   * methods.
   */
  protected class MouseHandler extends MouseAdapter
      implements MouseMotionListener {

    /**
     * Starts the dragging session by creating the appropriate instance
     * of DragController.
     */
    public void mousePressed(MouseEvent e) {
      if ((e.getSource() == BasicSplitPaneDivider.this ||
          e.getSource() == splitPane) &&
          dragger == null && splitPane.isEnabled()) {
        Component newHiddenDivider = splitPaneUI.
            getNonContinuousLayoutDivider();

        if (hiddenDivider != newHiddenDivider) {
          if (hiddenDivider != null) {
            hiddenDivider.removeMouseListener(this);
            hiddenDivider.removeMouseMotionListener(this);
          }
          hiddenDivider = newHiddenDivider;
          if (hiddenDivider != null) {
            hiddenDivider.addMouseMotionListener(this);
            hiddenDivider.addMouseListener(this);
          }
        }
        if (splitPane.getLeftComponent() != null &&
            splitPane.getRightComponent() != null) {
          if (orientation == JSplitPane.HORIZONTAL_SPLIT) {
            dragger = new DragController(e);
          } else {
            dragger = new VerticalDragController(e);
          }
          if (!dragger.isValid()) {
            dragger = null;
          } else {
            prepareForDragging();
            dragger.continueDrag(e);
          }
        }
        e.consume();
      }
    }


    /**
     * If dragger is not null it is messaged with completeDrag.
     */
    public void mouseReleased(MouseEvent e) {
      if (dragger != null) {
        if (e.getSource() == splitPane) {
          dragger.completeDrag(e.getX(), e.getY());
        } else if (e.getSource() == BasicSplitPaneDivider.this) {
          Point ourLoc = getLocation();

          dragger.completeDrag(e.getX() + ourLoc.x,
              e.getY() + ourLoc.y);
        } else if (e.getSource() == hiddenDivider) {
          Point hDividerLoc = hiddenDivider.getLocation();
          int ourX = e.getX() + hDividerLoc.x;
          int ourY = e.getY() + hDividerLoc.y;

          dragger.completeDrag(ourX, ourY);
        }
        dragger = null;
        e.consume();
      }
    }

    //
    // MouseMotionListener
    //

    /**
     * If dragger is not null it is messaged with continueDrag.
     */
    public void mouseDragged(MouseEvent e) {
      if (dragger != null) {
        if (e.getSource() == splitPane) {
          dragger.continueDrag(e.getX(), e.getY());
        } else if (e.getSource() == BasicSplitPaneDivider.this) {
          Point ourLoc = getLocation();

          dragger.continueDrag(e.getX() + ourLoc.x,
              e.getY() + ourLoc.y);
        } else if (e.getSource() == hiddenDivider) {
          Point hDividerLoc = hiddenDivider.getLocation();
          int ourX = e.getX() + hDividerLoc.x;
          int ourY = e.getY() + hDividerLoc.y;

          dragger.continueDrag(ourX, ourY);
        }
        e.consume();
      }
    }


    /**
     * Resets the cursor based on the orientation.
     */
    public void mouseMoved(MouseEvent e) {
    }

    /**
     * Invoked when the mouse enters a component.
     *
     * @param e MouseEvent describing the details of the enter event.
     * @since 1.5
     */
    public void mouseEntered(MouseEvent e) {
      if (e.getSource() == BasicSplitPaneDivider.this) {
        setMouseOver(true);
      }
    }

    /**
     * Invoked when the mouse exits a component.
     *
     * @param e MouseEvent describing the details of the exit event.
     * @since 1.5
     */
    public void mouseExited(MouseEvent e) {
      if (e.getSource() == BasicSplitPaneDivider.this) {
        setMouseOver(false);
      }
    }
  }


  /**
   * Handles the events during a dragging session for a
   * HORIZONTAL_SPLIT oriented split pane. This continually
   * messages <code>dragDividerTo</code> and then when done messages
   * <code>finishDraggingTo</code>. When an instance is created it should be
   * messaged with <code>isValid</code> to insure that dragging can happen
   * (dragging won't be allowed if the two views can not be resized).
   * <p>
   * <strong>Warning:</strong>
   * Serialized objects of this class will not be compatible with
   * future Swing releases. The current serialization support is
   * appropriate for short term storage or RMI between applications running
   * the same version of Swing.  As of 1.4, support for long term storage
   * of all JavaBeans&trade;
   * has been added to the <code>java.beans</code> package.
   * Please see {@link java.beans.XMLEncoder}.
   */
  protected class DragController {

    /**
     * Initial location of the divider.
     */
    int initialX;

    /**
     * Maximum and minimum positions to drag to.
     */
    int maxX, minX;

    /**
     * Initial location the mouse down happened at.
     */
    int offset;


    protected DragController(MouseEvent e) {
      JSplitPane splitPane = splitPaneUI.getSplitPane();
      Component leftC = splitPane.getLeftComponent();
      Component rightC = splitPane.getRightComponent();

      initialX = getLocation().x;
      if (e.getSource() == BasicSplitPaneDivider.this) {
        offset = e.getX();
      } else { // splitPane
        offset = e.getX() - initialX;
      }
      if (leftC == null || rightC == null || offset < -1 ||
          offset >= getSize().width) {
        // Don't allow dragging.
        maxX = -1;
      } else {
        Insets insets = splitPane.getInsets();

        if (leftC.isVisible()) {
          minX = leftC.getMinimumSize().width;
          if (insets != null) {
            minX += insets.left;
          }
        } else {
          minX = 0;
        }
        if (rightC.isVisible()) {
          int right = (insets != null) ? insets.right : 0;
          maxX = Math.max(0, splitPane.getSize().width -
              (getSize().width + right) -
              rightC.getMinimumSize().width);
        } else {
          int right = (insets != null) ? insets.right : 0;
          maxX = Math.max(0, splitPane.getSize().width -
              (getSize().width + right));
        }
        if (maxX < minX) {
          minX = maxX = 0;
        }
      }
    }


    /**
     * Returns true if the dragging session is valid.
     */
    protected boolean isValid() {
      return (maxX > 0);
    }


    /**
     * Returns the new position to put the divider at based on
     * the passed in MouseEvent.
     */
    protected int positionForMouseEvent(MouseEvent e) {
      int newX = (e.getSource() == BasicSplitPaneDivider.this) ?
          (e.getX() + getLocation().x) : e.getX();

      newX = Math.min(maxX, Math.max(minX, newX - offset));
      return newX;
    }


    /**
     * Returns the x argument, since this is used for horizontal
     * splits.
     */
    protected int getNeededLocation(int x, int y) {
      int newX;

      newX = Math.min(maxX, Math.max(minX, x - offset));
      return newX;
    }


    protected void continueDrag(int newX, int newY) {
      dragDividerTo(getNeededLocation(newX, newY));
    }


    /**
     * Messages dragDividerTo with the new location for the mouse
     * event.
     */
    protected void continueDrag(MouseEvent e) {
      dragDividerTo(positionForMouseEvent(e));
    }


    protected void completeDrag(int x, int y) {
      finishDraggingTo(getNeededLocation(x, y));
    }


    /**
     * Messages finishDraggingTo with the new location for the mouse
     * event.
     */
    protected void completeDrag(MouseEvent e) {
      finishDraggingTo(positionForMouseEvent(e));
    }
  } // End of BasicSplitPaneDivider.DragController


  /**
   * Handles the events during a dragging session for a
   * VERTICAL_SPLIT oriented split pane. This continually
   * messages <code>dragDividerTo</code> and then when done messages
   * <code>finishDraggingTo</code>. When an instance is created it should be
   * messaged with <code>isValid</code> to insure that dragging can happen
   * (dragging won't be allowed if the two views can not be resized).
   */
  protected class VerticalDragController extends DragController {

    /* DragControllers ivars are now in terms of y, not x. */
    protected VerticalDragController(MouseEvent e) {
      super(e);
      JSplitPane splitPane = splitPaneUI.getSplitPane();
      Component leftC = splitPane.getLeftComponent();
      Component rightC = splitPane.getRightComponent();

      initialX = getLocation().y;
      if (e.getSource() == BasicSplitPaneDivider.this) {
        offset = e.getY();
      } else {
        offset = e.getY() - initialX;
      }
      if (leftC == null || rightC == null || offset < -1 ||
          offset > getSize().height) {
        // Don't allow dragging.
        maxX = -1;
      } else {
        Insets insets = splitPane.getInsets();

        if (leftC.isVisible()) {
          minX = leftC.getMinimumSize().height;
          if (insets != null) {
            minX += insets.top;
          }
        } else {
          minX = 0;
        }
        if (rightC.isVisible()) {
          int bottom = (insets != null) ? insets.bottom : 0;

          maxX = Math.max(0, splitPane.getSize().height -
              (getSize().height + bottom) -
              rightC.getMinimumSize().height);
        } else {
          int bottom = (insets != null) ? insets.bottom : 0;

          maxX = Math.max(0, splitPane.getSize().height -
              (getSize().height + bottom));
        }
        if (maxX < minX) {
          minX = maxX = 0;
        }
      }
    }


    /**
     * Returns the y argument, since this is used for vertical
     * splits.
     */
    protected int getNeededLocation(int x, int y) {
      int newY;

      newY = Math.min(maxX, Math.max(minX, y - offset));
      return newY;
    }


    /**
     * Returns the new position to put the divider at based on
     * the passed in MouseEvent.
     */
    protected int positionForMouseEvent(MouseEvent e) {
      int newY = (e.getSource() == BasicSplitPaneDivider.this) ?
          (e.getY() + getLocation().y) : e.getY();

      newY = Math.min(maxX, Math.max(minX, newY - offset));
      return newY;
    }
  } // End of BasicSplitPaneDividier.VerticalDragController


  /**
   * Used to layout a <code>BasicSplitPaneDivider</code>.
   * Layout for the divider
   * involves appropriately moving the left/right buttons around.
   */
  protected class DividerLayout implements LayoutManager {

    public void layoutContainer(Container c) {
      if (leftButton != null && rightButton != null &&
          c == BasicSplitPaneDivider.this) {
        if (splitPane.isOneTouchExpandable()) {
          Insets insets = getInsets();

          if (orientation == JSplitPane.VERTICAL_SPLIT) {
            int extraX = (insets != null) ? insets.left : 0;
            int blockSize = getHeight();

            if (insets != null) {
              blockSize -= (insets.top + insets.bottom);
              blockSize = Math.max(blockSize, 0);
            }
            blockSize = Math.min(blockSize, oneTouchSize);

            int y = (c.getSize().height - blockSize) / 2;

            if (!centerOneTouchButtons) {
              y = (insets != null) ? insets.top : 0;
              extraX = 0;
            }
            leftButton.setBounds(extraX + oneTouchOffset, y,
                blockSize * 2, blockSize);
            rightButton.setBounds(extraX + oneTouchOffset +
                    oneTouchSize * 2, y,
                blockSize * 2, blockSize);
          } else {
            int extraY = (insets != null) ? insets.top : 0;
            int blockSize = getWidth();

            if (insets != null) {
              blockSize -= (insets.left + insets.right);
              blockSize = Math.max(blockSize, 0);
            }
            blockSize = Math.min(blockSize, oneTouchSize);

            int x = (c.getSize().width - blockSize) / 2;

            if (!centerOneTouchButtons) {
              x = (insets != null) ? insets.left : 0;
              extraY = 0;
            }

            leftButton.setBounds(x, extraY + oneTouchOffset,
                blockSize, blockSize * 2);
            rightButton.setBounds(x, extraY + oneTouchOffset +
                    oneTouchSize * 2, blockSize,
                blockSize * 2);
          }
        } else {
          leftButton.setBounds(-5, -5, 1, 1);
          rightButton.setBounds(-5, -5, 1, 1);
        }
      }
    }


    public Dimension minimumLayoutSize(Container c) {
      // NOTE: This isn't really used, refer to
      // BasicSplitPaneDivider.getPreferredSize for the reason.
      // I leave it in hopes of having this used at some point.
      if (c != BasicSplitPaneDivider.this || splitPane == null) {
        return new Dimension(0, 0);
      }
      Dimension buttonMinSize = null;

      if (splitPane.isOneTouchExpandable() && leftButton != null) {
        buttonMinSize = leftButton.getMinimumSize();
      }

      Insets insets = getInsets();
      int width = getDividerSize();
      int height = width;

      if (orientation == JSplitPane.VERTICAL_SPLIT) {
        if (buttonMinSize != null) {
          int size = buttonMinSize.height;
          if (insets != null) {
            size += insets.top + insets.bottom;
          }
          height = Math.max(height, size);
        }
        width = 1;
      } else {
        if (buttonMinSize != null) {
          int size = buttonMinSize.width;
          if (insets != null) {
            size += insets.left + insets.right;
          }
          width = Math.max(width, size);
        }
        height = 1;
      }
      return new Dimension(width, height);
    }


    public Dimension preferredLayoutSize(Container c) {
      return minimumLayoutSize(c);
    }


    public void removeLayoutComponent(Component c) {
    }

    public void addLayoutComponent(String string, Component c) {
    }
  } // End of class BasicSplitPaneDivider.DividerLayout


  /**
   * Listeners installed on the one touch expandable buttons.
   */
  private class OneTouchActionHandler implements ActionListener {

    /**
     * True indicates the resize should go the minimum (top or left)
     * vs false which indicates the resize should go to the maximum.
     */
    private boolean toMinimum;

    OneTouchActionHandler(boolean toMinimum) {
      this.toMinimum = toMinimum;
    }

    public void actionPerformed(ActionEvent e) {
      Insets insets = splitPane.getInsets();
      int lastLoc = splitPane.getLastDividerLocation();
      int currentLoc = splitPaneUI.getDividerLocation(splitPane);
      int newLoc;

      // We use the location from the UI directly, as the location the
      // JSplitPane itself maintains is not necessarly correct.
      if (toMinimum) {
        if (orientation == JSplitPane.VERTICAL_SPLIT) {
          if (currentLoc >= (splitPane.getHeight() -
              insets.bottom - getHeight())) {
            int maxLoc = splitPane.getMaximumDividerLocation();
            newLoc = Math.min(lastLoc, maxLoc);
            splitPaneUI.setKeepHidden(false);
          } else {
            newLoc = insets.top;
            splitPaneUI.setKeepHidden(true);
          }
        } else {
          if (currentLoc >= (splitPane.getWidth() -
              insets.right - getWidth())) {
            int maxLoc = splitPane.getMaximumDividerLocation();
            newLoc = Math.min(lastLoc, maxLoc);
            splitPaneUI.setKeepHidden(false);
          } else {
            newLoc = insets.left;
            splitPaneUI.setKeepHidden(true);
          }
        }
      } else {
        if (orientation == JSplitPane.VERTICAL_SPLIT) {
          if (currentLoc == insets.top) {
            int maxLoc = splitPane.getMaximumDividerLocation();
            newLoc = Math.min(lastLoc, maxLoc);
            splitPaneUI.setKeepHidden(false);
          } else {
            newLoc = splitPane.getHeight() - getHeight() -
                insets.top;
            splitPaneUI.setKeepHidden(true);
          }
        } else {
          if (currentLoc == insets.left) {
            int maxLoc = splitPane.getMaximumDividerLocation();
            newLoc = Math.min(lastLoc, maxLoc);
            splitPaneUI.setKeepHidden(false);
          } else {
            newLoc = splitPane.getWidth() - getWidth() -
                insets.left;
            splitPaneUI.setKeepHidden(true);
          }
        }
      }
      if (currentLoc != newLoc) {
        splitPane.setDividerLocation(newLoc);
        // We do this in case the dividers notion of the location
        // differs from the real location.
        splitPane.setLastDividerLocation(currentLoc);
      }
    }
  } // End of class BasicSplitPaneDivider.LeftActionListener
}
